package org.example.auth.service;

import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.example.api.system.RemoteTenantService;
import org.example.api.system.domain.vo.RemoteTenantVo;
import org.example.auth.config.properties.UserPasswordProperties;
import org.example.auth.event.LoginLogEvent;
import org.example.common.core.constants.Constants;
import org.example.common.core.constants.GlobalConstants;
import org.example.common.core.constants.TenantConstants;
import org.example.common.core.exception.ServiceException;
import org.example.common.core.exception.oauth.UserException;
import org.example.common.core.utils.MessageUtils;
import org.example.common.core.utils.ServletUtils;
import org.example.common.core.utils.SpringUtils;
import org.example.common.core.utils.StringUtils;
import org.example.common.redis.utils.RedisUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;
import java.util.function.Supplier;

/**
 * 登录校验方法
 *
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class SysLoginService {

    @DubboReference
    private RemoteTenantService remoteTenantService;
    private final UserPasswordProperties userPasswordProperties;
    /**
     * 校验租户
     *
     * @param tenantId 租户ID
     */
    public void checkTenant(String tenantId) {
        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
            return;
        }
        if (StringUtils.isBlank(tenantId)) {
            throw new ServiceException(MessageUtils.message("tenant.number.not.blank"));
        }
        RemoteTenantVo tenant = remoteTenantService.queryByTenantId(tenantId);
        if (ObjectUtil.isNull(tenant)) {
            log.info("登录租户：{} 不存在.", tenantId);
            throw new ServiceException(MessageUtils.message("tenant.not.exists"));
        } else if (TenantConstants.DISABLE.equals(tenant.getStatus())) {
            log.info("登录租户：{} 已被停用.", tenantId);
            throw new ServiceException(MessageUtils.message("tenant.blocked"));
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
            && new Date().after(tenant.getExpireTime())) {
            log.info("登录租户：{} 已超过有效期.", tenantId);
            throw new ServiceException(MessageUtils.message("tenant.expired"));
        }
    }



    @Async
    public void recordLogininfor(String tenantId, String username,String device_type,String status, String message) {
        // 封装对象
        LoginLogEvent loginLogEvent = new LoginLogEvent();
        loginLogEvent.setTenantId(tenantId);
        loginLogEvent.setUsername(username);
        loginLogEvent.setDeviceType(device_type);
        loginLogEvent.setStatus(status);
        loginLogEvent.setMessage(message);
        loginLogEvent.setRequest(ServletUtils.getRequest());
        SpringUtils.context().publishEvent(loginLogEvent);
    }


    /**
     * 登录校验
     */
    public void checkLogin(String grant_type,String tenantId, String username,String device_type, Supplier<Boolean> supplier) {
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;
        Integer maxRetryCount = userPasswordProperties.getMaxRetryCount();
        Integer lockTime = userPasswordProperties.getLockTime();
        /**
         * 登录重试超出限制提示
         */
        String retryLimitExceed=String.format("user.%s.retry.limit.exceed",grant_type);
        /**
         * 登录重试限制计数提示
         */
        String retryLimitCount=String.format("user.%s.retry.limit.count",grant_type);

        // 获取用户登录错误次数，默认为0 (可自定义限制策略 例如: key + username + ip)
        int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
        // 锁定时间内登录 则踢出
        if (errorNumber >= maxRetryCount) {
            recordLogininfor(tenantId, username,device_type, loginFail, MessageUtils.message(retryLimitExceed, maxRetryCount, lockTime));
            throw new UserException(retryLimitExceed, maxRetryCount, lockTime);
        }
        if (supplier.get()) {
            // 错误次数递增
            errorNumber++;
            RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
            // 达到规定错误次数 则锁定登录
            if (errorNumber >= maxRetryCount) {
                recordLogininfor(tenantId, username,device_type, loginFail, MessageUtils.message(retryLimitExceed, maxRetryCount, lockTime));
                throw new UserException(retryLimitExceed, maxRetryCount, lockTime);
            } else {
                // 未达到规定错误次数
                recordLogininfor(tenantId, username,device_type, loginFail, MessageUtils.message(retryLimitCount, errorNumber));
                throw new UserException(retryLimitCount, errorNumber);
            }
        }

        // 登录成功 清空错误次数
        RedisUtils.deleteObject(errorKey);
    }


}
